/* 
    Assume the bitstream size is known in file "test.info", 720x480 resolution, 10 frames
    sample code for H264 for bitstream input and YUV output
    This sample code is to do decode 100 rounds,10 frames/round, named "/tmp/dev0.yuv"
    #./h264_dec_main test.264 test.info
 */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/videodev.h>
#include <errno.h>
#include <linux/fb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <signal.h>
#include <sys/time.h>

#include "favc_avcodec.h"

#define FRAME_COUNT 10
#define TEST_ROUND  100
#define TEST_WIDTH  720
#define TEST_HEIGHT 480

#define FAVC_DECODER_DEV  "/dev/f264dec"
#define dout_name         "/tmp/dev0.yuv"


int frame_info[FRAME_COUNT];

typedef struct AVFrame {
    uint8_t *data[4];
} AVFrame;


typedef struct video_profile
{
    unsigned int bit_rate;
    unsigned int width;   //length per dma buffer
    unsigned int height;
    unsigned int framerate;
    unsigned int frame_rate_base;
    unsigned int gop_size;
    unsigned int qmax;
    unsigned int qmin;   
    unsigned int quant;
    unsigned int intra;
    AVFrame *coded_frame;
    char *priv;
} video_profile;


#define         MAX_BUF_SIZE (720*576*3/2)
FILE            *din,*din_info;
FILE            *dout;
int             favc_dec_fd=0;
int             dec_mmap_addr;
unsigned int    in_virt_buffer;


typedef unsigned long long uint64;

struct  timeval t1;
uint64  nsecs;
void time_start(struct timeval *tv)
{
    gettimeofday (tv, NULL);
}

uint64 time_delta(struct timeval *start, struct timeval *stop)
{
    uint64 secs, usecs;
    
    secs = stop->tv_sec - start->tv_sec;
    usecs = stop->tv_usec - start->tv_usec;
    if (usecs < 0)
    {
        secs--;
        usecs += 1000000;
    }
    return secs * 1000000 + usecs;
}

uint64 time_stop(struct timeval *tv)
{
    struct timeval end;
    gettimeofday (&end, NULL);
    return time_delta (tv, &end);
}


int h264_init(video_profile *video_setting)
{
    FAVC_DEC_PARAM      tDecParam;
    int                 ret_value;


    if(favc_dec_fd==0)
      favc_dec_fd=open(FAVC_DECODER_DEV,O_RDWR);
	
    if(favc_dec_fd == 0)
    {
	    printf("Fail to open %s\n",FAVC_DECODER_DEV);
	    return -1;
    }    

    dec_mmap_addr=(int)mmap(0,(video_setting->width*video_setting->height),
        PROT_READ|PROT_WRITE,MAP_SHARED,favc_dec_fd, 0);
    if(dec_mmap_addr<=0)
    {
        printf("Fail to allocate mmap address\n");
        return -1;
    }
//printf("mmap addr=0x%x\n",dec_mmap_addr);

//	tDecParam.u32API_version = 0x00020002;
	tDecParam.u32MaxWidth = video_setting->width;
	tDecParam.u32MaxHeight = video_setting->height;
	tDecParam.u32FrameBufferWidth = video_setting->width;
	tDecParam.u32FrameBufferHeight = video_setting->height;

	ret_value=ioctl(favc_dec_fd,FAVC_IOCTL_DECODE_INIT,&tDecParam);
	if(ret_value<0)
	{
	    printf("FAVC_IOCTL_DECODE_INIT: memory allocation failed\n");
	    return -1;
	}
    
	return 0;
}



int h264_close(video_profile *video_setting)
{

	if(favc_dec_fd)
	{
	    munmap(dec_mmap_addr,(video_setting->width*video_setting->height*3)/2);
		close(favc_dec_fd);
	}
	favc_dec_fd = 0;
	return 0;
}

void favc_decode(video_profile *video_setting, unsigned char *frame, void *data, int size)
{
    AVFrame             *pict=(AVFrame *)data;
    FAVC_DEC_PARAM      tDecParam;
    int  ret_value;
    
	// set display virtual addr (YUV or RGB)
//printf("pict=0x%x data=0x%x\n",pict,data);fflush(stdout);
//YUV422 needn't 
	tDecParam.pu8Display_addr[0] = (unsigned int)pict->data[0];
//	tDecParam.pu8Display_addr[1] = (unsigned int)pict->data[1];
//	tDecParam.pu8Display_addr[2] = (unsigned int)pict->data[2];
	tDecParam.u32Pkt_size =	(unsigned int)size;
	tDecParam.pu8Pkt_buf = (unsigned int)frame;
//printf("YUV 0x%x 0x%x 0x%x size %d buf 0x%x\n",tDecParam.pu8Display_addr[0],tDecParam.pu8Display_addr[1],
//    tDecParam.pu8Display_addr[2],tDecParam.u32Pkt_size,tDecParam.pu8Pkt_buf);fflush(stdout);

	if((ret_value = ioctl(favc_dec_fd,FAVC_IOCTL_DECODE_FRAME,&tDecParam))<0)
	{
	    //*got_picture = 0;		
	    printf("FAVC_IOCTL_DECODE_FRAME: Failed.\n");
		return;
	}
    return;
}


int main(int argc, char **argv)
{
    int i,totalsz=0;
    int fcount=0;
    video_profile   video_setting;
    AVFrame pict;
    
    dout=fopen(dout_name,"wb");
    printf("Use encoder output name %s\n",dout_name);

    if(argc<3)
        exit(0);
    printf("Input File Name %s\n",argv[1]);
    din=fopen(argv[1],"rb");

    printf("Input File Info %s\n",argv[2]);
    din_info=fopen(argv[2],"r");

    //set the default value
    video_setting.qmax = 51;
    video_setting.qmin = 0;
    video_setting.quant = 0;
    video_setting.bit_rate = 512;
    video_setting.width = TEST_WIDTH;
    video_setting.height = TEST_HEIGHT;
    video_setting.framerate = 30;
    video_setting.frame_rate_base = 1;
    video_setting.gop_size = 60;

    memset(frame_info,0,sizeof(frame_info));
    for(i=0;i<FRAME_COUNT;i++)
    {
        fscanf(din_info,"%d",&frame_info[i]);
        totalsz+=frame_info[i];
printf("%d ",frame_info[i]);
    }
printf("\n");
    fclose(din_info);

   	in_virt_buffer = (unsigned int)malloc(video_setting.width*video_setting.height*10);

    dout=fopen(dout_name,"wb");
    fread((void *)in_virt_buffer,totalsz,1,din);
    
    time_start(&t1);
    fcount=i=0;
    while(1)
    {
       // fcount=i=0;
        //while(fcount<TEST_ROUND*FRAME_COUNT)
        {
            int runsz=0;
            
            if(((fcount%20)==0)&&(fcount!=0))
            {
                nsecs = time_stop(&t1) / 1000;
                printf("[%06d] Spend %06lld ms, %ffps\n",fcount,nsecs,(float)((float)fcount)/((float)nsecs/(float)1000.0));
                fflush(stdout);
            }
               
            h264_init(&video_setting);
    
            //YUV422 needn't assign U,V
            pict.data[0]=(unsigned char *)dec_mmap_addr;
            i=0;
            while(i<FRAME_COUNT)
            {
                favc_decode(&video_setting,(unsigned char *)(in_virt_buffer+runsz),(void *)&pict,frame_info[i]);
                runsz+=frame_info[i];
                i++;
                fcount++;
            }
            h264_close(&video_setting);
        }
    }
    
    time_stop(&t1);
    nsecs = time_stop(&t1) / 1000;
    printf("\nspend %lld milliseconds with toal frame count %d = %f(frames/per second)\n", nsecs,fcount,(float)((float)fcount)/((float)nsecs/(float)1000.0));
    fflush(stdout);
	fclose(din);
	fclose(dout);
	free((void *)in_virt_buffer);
	return 0;
}
